<template>
    <section 
        :id="containerId" 
        :class="containerClass" 
        :style="containerStyle" 
        v-if="containerIsShow"
    >
        <transition name="tinylayer-fade">
            <div 
                v-if="maskVisible && mode !== 'loading' && layerIsShow"
                class="tinylayer-mask" 
                :style="maskStyle" 
                @click="close( 'mask' )"
            ></div>
        </transition>
        <transition name="tinylayer-fade" @after-leave="publicAfterLeave( 'loading' )">
            <div 
                class="tinylayer-public-loading" 
                :style="{ background: loading.background }" 
                v-if="mode === 'loading' && layerIsShow"
            >
                <div>
                    <div v-html="loadingSVG"></div>
                    <p v-if="loading.text">{{ loading.text }}</p>
                </div>
            </div>
        </transition>
        <transition name="tinylayer-slide" @after-leave="publicAfterLeave( 'alert' )">
            <div 
                class="tinylayer-public-wrapper tinylayer-alert-confirm" 
                v-if="mode === 'alert' && layerIsShow"
            >
                <div>
                    <div class="tinylayer-alert-confirm-header">
                        <i 
                            :class="{ [ 'tinylayer-alert-confirm-' + alert.type ]: true }" 
                            :style="{ width: svgSize + 'px', height: svgSize + 'px' }"
                        >
                            <svg viewBox="0 0 1024 1024" :width="svgSize" :height="svgSize">
                                <path d="M512 34.133333C249.173333 34.133333 34.133333 249.173333 34.133333 512s215.04 477.866667 477.866667 477.866667 477.866667-215.04 477.866667-477.866667S774.826667 34.133333 512 34.133333z m34.133333 670.72c0 18.773333-15.36 34.133333-34.133333 34.133334s-34.133333-15.36-34.133333-34.133334v-187.733333c0-18.773333 15.36-34.133333 34.133333-34.133333s34.133333 15.36 34.133333 34.133333v187.733333z m-34.133333-283.306666c-37.546667 0-68.266667-30.72-68.266667-68.266667s30.72-68.266667 68.266667-68.266667 68.266667 30.72 68.266667 68.266667-30.72 68.266667-68.266667 68.266667z" :fill="alert.type === 'info' ? '#1588fc' : '#f8a528'" v-if="alert.type === 'info' || alert.type === 'warning'"></path>
                                <path d="M512 34.133333C247.466667 34.133333 34.133333 247.466667 34.133333 512s213.333333 477.866667 477.866667 477.866667 477.866667-213.333333 477.866667-477.866667S776.533333 34.133333 512 34.133333z m228.693333 356.693334l-290.133333 290.133333c-6.826667 6.826667-15.36 10.24-23.893333 10.24s-17.066667-3.413333-23.893334-10.24l-119.466666-119.466667c-13.653333-13.653333-13.653333-34.133333 0-47.786666 13.653333-13.653333 35.84-13.653333 47.786666 0l95.573334 95.573333 266.24-266.24c13.653333-13.653333 34.133333-13.653333 47.786666 0 13.653333 13.653333 13.653333 34.133333 0 47.786667z" fill="#24d472" v-if="alert.type === 'success'"></path>
                                <path d="M512 34.133333C247.466667 34.133333 34.133333 247.466667 34.133333 512s213.333333 477.866667 477.866667 477.866667 477.866667-213.333333 477.866667-477.866667S776.533333 34.133333 512 34.133333z m194.56 624.64c13.653333 13.653333 13.653333 34.133333 0 47.786667-6.826667 6.826667-15.36 10.24-23.893333 10.24s-17.066667-3.413333-23.893334-10.24L512 559.786667l-146.773333 146.773333c-13.653333 13.653333-35.84 13.653333-47.786667 0-13.653333-13.653333-13.653333-34.133333 0-47.786667l146.773333-146.773333-146.773333-146.773333c-13.653333-13.653333-13.653333-34.133333 0-47.786667 13.653333-13.653333 34.133333-13.653333 47.786667 0l146.773333 146.773333 146.773333-146.773333c13.653333-13.653333 34.133333-13.653333 47.786667 0 13.653333 13.653333 13.653333 34.133333 0 47.786667L559.786667 512l146.773333 146.773333z" fill="#f43f26" v-if="alert.type === 'error'"></path>
                            </svg>
                        </i>
                        <p>{{ alert.title }}</p>
                    </div>
                    <div class="tinylayer-alert-confirm-content">{{ alert.content }}</div>
                    <div class="tinylayer-public-button-box tinylayer-alert-confirm-button">
                        <button class="tinylayer-public-button-ok" @click="layerIsShow = false">{{ alert.ok.text }}</button>
                    </div>
                </div>
            </div>
        </transition>
        <transition name="tinylayer-slide" @after-leave="publicAfterLeave( 'confirm' )">
            <div 
                class="tinylayer-public-wrapper tinylayer-alert-confirm" 
                v-if="mode === 'confirm' && layerIsShow"
            >
                <div>
                    <div class="tinylayer-alert-confirm-header">
                        <i 
                        	class="tinylayer-alert-confirm-warning" 
                        	:style="{ width: svgSize + 'px', height: svgSize + 'px' }"
                        >
                            <svg viewBox="0 0 1024 1024" :width="svgSize" :height="svgSize">
                                <path d="M512 34.133333C249.173333 34.133333 34.133333 249.173333 34.133333 512s215.04 477.866667 477.866667 477.866667 477.866667-215.04 477.866667-477.866667S774.826667 34.133333 512 34.133333z m34.133333 670.72c0 18.773333-15.36 34.133333-34.133333 34.133334s-34.133333-15.36-34.133333-34.133334v-187.733333c0-18.773333 15.36-34.133333 34.133333-34.133333s34.133333 15.36 34.133333 34.133333v187.733333z m-34.133333-283.306666c-37.546667 0-68.266667-30.72-68.266667-68.266667s30.72-68.266667 68.266667-68.266667 68.266667 30.72 68.266667 68.266667-30.72 68.266667-68.266667 68.266667z" fill="#f8a528"></path>
                            </svg>
                        </i>
                        <p>{{ confirm.title }}</p>
                    </div>
                    <div class="tinylayer-alert-confirm-content">{{ confirm.content }}</div>
                    <div class="tinylayer-public-button-box tinylayer-alert-confirm-button">
                        <button 
                            class="tinylayer-public-button-cancel" 
                            @click="confirmCancel"
                        >{{ confirm.cancel.text }}</button>
                        <button 
                            class="tinylayer-public-button-ok" 
                            @click="confirmOk"
                        >{{ confirm.ok.text }}</button>
                    </div>
                </div>
            </div>
        </transition>
        <transition name="tinylayer-slide" @after-leave="publicAfterLeave( 'prompt' )" @after-enter="afterPromptEnter">
            <div 
                class="tinylayer-public-wrapper tinylayer-prompt" 
                v-if="mode === 'prompt' && layerIsShow"
            >
                <div>
                    <div class="tinylayer-prompt-title">{{ prompt.title }}</div>
                    <div :class="promptInputClass">
                        <input 
                            ref="promptRef"
                            spellcheck="false" 
                            autocomplete="off"
                            :type="/(text|password)/.test( prompt.inputType ) ? prompt.inputType : 'text'"
                            :value="prompt.defaultValue"
                            :placeholder="prompt.placeholder"
                            :disabled="prompt.disabled"
                        >
                    </div>
                    <div class="tinylayer-error-msg" v-if="prompt.errorMsg && prompt.isError">{{ prompt.errorMsg }}</div>
                    <div class="tinylayer-public-button-box">
                        <button class="tinylayer-public-button-cancel" @click="promptCancel">{{ prompt.cancel.text }}</button>
                        <button class="tinylayer-public-button-ok" @click="promptOk">{{ prompt.ok.text }}</button>
                    </div>
                </div>
            </div>
        </transition>
        <transition name="tinylayer-slide" @after-enter="afterWrapperEnter" @after-leave="afterWrapperLeave">
            <div 
                ref="wrapperRef" 
                :class="wrapperClass" 
                :style="wrapperStyle" 
                v-show="mode === '' && layerIsShow"
            >
                <div 
                    ref="headerRef" 
                    class="tinylayer-header" 
                    :style="headerStyle" 
                    v-if="headerVisible"
                >
                    <slot name="header">
                        <div class="tinylayer-title">
                            <span>{{ title }}</span>
                        </div>
                        <div class="tinylayer-iconbtn">
                            <i 
                                class="tinylayer-icon tinylayer-fullscreen" 
                                v-if="fullscreenIconVisible" 
                                @click="fullscreenToggle"
                            >
                                <svg viewBox="0 0 1024 1024" width="16" height="16">
                                    <path d="M181 357.5V181.2h176.4c14.3 0 25.9-11.6 25.9-25.9v-31.1c0-14.3-11.6-25.9-25.9-25.9H118c-11 0-20 9-20 20v239.4c0 14.3 11.6 25.9 25.9 25.9H155c14.4-0.1 26-11.7 26-26.1zM668.6 181.2H845v176.4c0 14.3 11.6 25.9 25.9 25.9H902c14.3 0 25.9-11.6 25.9-25.9V118.2c0-11-9-20-20-20H668.6c-14.3 0-25.9 11.6-25.9 25.9v31.1c0 14.3 11.6 26 25.9 26zM357.4 845.2H181V668.8c0-14.3-11.6-25.9-25.9-25.9H124c-14.3 0-25.9 11.6-25.9 25.9v239.4c0 11 9 20 20 20h239.4c14.3 0 25.9-11.6 25.9-25.9v-31.1c-0.1-14.4-11.7-26-26-26zM845 668.8v176.4H668.6c-14.3 0-25.9 11.6-25.9 25.9v31.1c0 14.3 11.6 25.9 25.9 25.9H908c11 0 20-9 20-20V668.8c0-14.3-11.6-25.9-25.9-25.9H871c-14.4 0-26 11.6-26 25.9z"></path>
                                </svg>
                            </i>
                            <i 
                                class="tinylayer-icon tinylayer-close" 
                                v-if="closeIconVisible" 
                                @click="close"
                            >
                                <svg viewBox="0 0 1024 1024" width="16" height="16">
                                    <path d="M550.848 502.496l308.64-308.896a31.968 31.968 0 1 0-45.248-45.248l-308.608 308.896-308.64-308.928a31.968 31.968 0 1 0-45.248 45.248l308.64 308.896-308.64 308.896a31.968 31.968 0 1 0 45.248 45.248l308.64-308.896 308.608 308.896a31.968 31.968 0 1 0 45.248-45.248l-308.64-308.864z"></path>
                                </svg>
                            </i>
                        </div>
                    </slot>
                </div>
                <div 
                    class="tinylayer-content" 
                    :style="contentStyle"
                > 
                    <iframe 
                        :src="insertIframe" 
                        frameborder="0" 
                        width="100%" 
                        height="100%" 
                        class="tinylayer-content-iframe"
                        v-if="insertIframe"
                    ></iframe> 
                    <slot v-else></slot>
                </div>
                <div 
                    class="tinylayer-public-loading"
                    :style="contentLoadingStyle"
                    v-if="contentLoading"
                >
                    <div>
                    	<div v-html="loadingSVG"></div>
                        <p v-if="contentLoadingText">{{ contentLoadingText }}</p>
                    </div>
                </div> 
                <div 
                    class="tinylayer-footer" 
                    :style="footerStyle" 
                    v-if="footerVisible"
                >
                    <slot name="footer">
                        <div class="tinylayer-autoclose">
                        	<div v-if="Number.isSafeInteger( autoClose ) && autoClose > 0 && autoCloseIsShow">
                        		<span>{{ autoCloseText }}</span>
                        	</div>	
                        </div>
                        <div class="tinylayer-public-button-box">
                            <button 
                                class="tinylayer-public-button-cancel" 
                                :disabled="cancelDisabled"
                                v-if="cancelVisible" 
                                @click="cancel"
                            >{{ cancelText }}</button>
                            <button 
                                class="tinylayer-public-button-ok" 
                                :disabled="okDisabled"
                                v-if="okVisible"
                                @click="ok"
                            >
                                <i v-if="okLoading">
                                    <svg viewBox="0 0 1024 1024" width="16" height="16"><path d="M515.5 979.9c-19.33 0-35-15.67-35-35V782.8c0-19.33 15.67-35 35-35s35 15.67 35 35v162.1c0 19.33-15.67 35-35 35zM515.5 294c-19.33 0-35-15.67-35-35V96.9c0-19.33 15.67-35 35-35s35 15.67 35 35V259c0 19.33-15.67 35-35 35zM815.31 855.71c-8.96 0-17.91-3.42-24.75-10.25L675.94 730.84c-13.67-13.67-13.67-35.83 0-49.5 13.67-13.67 35.83-13.67 49.5 0l114.62 114.62c13.67 13.67 13.67 35.83 0 49.5a34.915 34.915 0 0 1-24.75 10.25zM330.31 370.71c-8.96 0-17.91-3.42-24.75-10.25L190.94 245.84c-13.67-13.67-13.67-35.83 0-49.5 13.67-13.67 35.83-13.67 49.5 0l114.62 114.62c13.67 13.67 13.67 35.83 0 49.5a34.915 34.915 0 0 1-24.75 10.25zM939.5 555.9H777.4c-19.33 0-35-15.67-35-35s15.67-35 35-35h162.1c19.33 0 35 15.67 35 35s-15.67 35-35 35zM253.6 555.9H91.5c-19.33 0-35-15.67-35-35s15.67-35 35-35h162.1c19.33 0 35 15.67 35 35s-15.67 35-35 35zM700.69 370.71c-8.96 0-17.91-3.42-24.75-10.25-13.67-13.67-13.67-35.83 0-49.5l114.62-114.62c13.67-13.67 35.83-13.67 49.5 0 13.67 13.67 13.67 35.83 0 49.5L725.44 360.46c-6.83 6.83-15.79 10.25-24.75 10.25zM215.69 855.71c-8.96 0-17.91-3.42-24.75-10.25-13.67-13.67-13.67-35.83 0-49.5l114.62-114.62c13.67-13.67 35.83-13.67 49.5 0s13.67 35.83 0 49.5L240.44 845.46a34.915 34.915 0 0 1-24.75 10.25z" fill="#ffffff"></path>
                                </svg>
                                </i>{{ okText }}</button>
                        </div>
                    </slot>
                </div>
            </div>
        </transition>
    </section>
</template>

<script>
    import util from "./util.js";
    import { isVue3 } from "vue-demi";

    const originalData = {
        loading: {
            text: "",
            zIndex: 2147483646,
            background: "rgba(255,255,255,.95)"
        },
        alert: {
            type: "",
            title: "",
            content: "",
            zIndex: 20000,
            maskBackground: "rgba(0,0,0,.7)",
            ok: {
                text: "确定"
            }
        },
        confirm: {
            title: "",
            content: "",
            zIndex: 20000,
            maskBackground: "rgba(0,0,0,.7)",
            ok: {
                text: "确定",
                callback: () => {}
            },
            cancel: {
                text: "取消",
                callback: () => {}
            }
        },
        prompt: {
            title: "",
            defaultValue: "",
            placeholder: "",
            inputType: "text",
            pattern: null,
            errorMsg: "",
            isError: false,
            zIndex: 20000,
            maskBackground: "rgba(0,0,0,.7)",
            ok: {
                text: "确定",
                closable: true,
                callback: () => {}
            },
            cancel: {
                text: "取消",
                callback: () => {}
            }
        }
    };

    const windowWidth = document.documentElement.clientWidth;
    const windowHeight = document.documentElement.clientHeight;

    let autoCloseTimer = null;
    let cacheWrapperTop = null;
    let cacheWrapperLeft = null;
    const headerHeight = "50px";
    const footerHeight = "60px";
    
    export default {
        name: "TinyLayer",
        model: {
            prop: "visible",
            event: "change"
        },
        props: {
            modelValue: {
                type: Boolean,
                default: false
            },
            visible: {
                type: Boolean,
                default: false
            },
            id: {
                type: String,
                default: ""
            },
            className: {
                type: String,
                default: ""
            },
            zIndex: {
                type: Number,
                default: 10000
            },
            maskVisible: {
                type: Boolean,
                default: true
            },
            maskClosable: {
                type: Boolean,
                default: false
            },
            maskBackground: {
                type: String,
                default: "rgba(0, 0, 0, .75)"
            },
            fullscreen: {
                type: Boolean,
                default: false
            },
            insertIframe: {
                type: String,
                default: ""
            },
            background: {
                type: String,
                default: "#fff"
            },
            autoClose: {
                type: Number,
                default: 0
            },
            autoCloseIntercept: {
                type: Boolean,
                default: false
            },
            destroyAfterClose: {
                type: Boolean,
                default: true
            },
            escClosable: {
                type: Boolean,
                default: true
            },
            draggable: {
                type: Boolean,
                default: false
            },
            bodyScrollable: {
                type: Boolean,
                default: true
            },
            headerVisible: {
                type: Boolean,
                default: true
            },
            footerVisible: {
                type: Boolean,
                default: true
            },
            contentBackground: {
                type: String,
                default: ""
            },
            contentLoading: {
                type: Boolean,
                default: false
            },
            contentLoadingText: {
                type: String,
                default: ""
            },
            contentLoadingBackground: {
                type: String,
                default: "rgba(255,255,255,.95)"
            },
            contentHeight: {
                type: String,
                default: "auto"
            },
            noline: {
                type: Boolean,
                default: false
            },
            title: {
                type: String,
                default: ""
            },
            width: {
                type: String,
                default: "450px"
            },
            closeIconVisible: {
                type: Boolean,
                default: true
            },
            fullscreenIconVisible: {
                type: Boolean,
                default: false
            },
            okText: {
                type: String,
                default: "确定"
            },
            okVisible: {
                type: Boolean,
                default: true
            },
            okDisabled: {
                type: Boolean,
                default: false  
            },
            okClosable: {
                type: Boolean,
                default: true
            },
            okLoading: {
                type: Boolean,
                default: false
            },
            cancelText: {
                type: String,
                default: "取消"
            },
            cancelVisible: {
                type: Boolean,
                default: true
            },
            cancelDisabled: {
                type: Boolean,
                default: false
            },
            cancelClosable: {
                type: Boolean,
                default: true
            }
        },
        data () {
            return {
                loading: util.clone( originalData.loading ),
                confirm: util.clone( originalData.confirm ),
                alert: util.clone( originalData.alert ),
                prompt: util.clone( originalData.prompt ),
                mode: "",
                containerIsShow: false,
                layerIsShow: false,
                wrapperTop: "50%",
                wrapperLeft: "50%",
                dragging: false,
                autoCloseText: "",
                autoCloseIsShow: false,
                fscreen: this.fullscreen,
                loadingSVG: `
                    <svg viewBox="25 25 50 50">
                        <circle cx="50" cy="50" r="20"></circle>
                    </svg>
                `,
                lineStyle: "#e0e0e0 solid 1px",
                svgSize: 26
            }
        },
        watch: {
            visible ( val ) {
                if ( val ) {
                    this.containerIsShow = true;
                    if ( this.isDefaultMode() ) {
                        if ( !this.bodyScrollable ) {
                            document.body.classList.add( "tinylayer-body-noscroll" );
                        }
                        if ( this.escClosable ) {
                            document.addEventListener("keyup", event => {
                                if ( 
                                    event.keyCode === 27 && 
                                    document.querySelector( "body > .tinylayer-default" ) 
                                ) {
                                    this.close();
                                }
                            })
                        }
                    }
                }
                util.delay().then(() => {
                    this.layerIsShow = val;
                })
            },
            modelValue ( val ) {
                if ( val ) {
                    this.containerIsShow = true;
                    if ( this.isDefaultMode() ) {
                        if ( !this.bodyScrollable ) {
                            document.body.classList.add( "tinylayer-body-noscroll" );
                        }
                        if ( this.escClosable ) {
                            document.addEventListener("keyup", event => {
                                if ( 
                                    event.keyCode === 27 && 
                                    document.querySelector( "body > .tinylayer-default" ) 
                                ) {
                                    this.close();
                                }
                            })
                        }
                    }
                }
                util.delay().then(() => {
                    this.layerIsShow = val;
                })
            },
            loading: {
            	handler ( val ) {
            		this.setDefaultData( "loading", val );
            	},
            	deep: true
            },
            alert: {
            	handler ( val ) {
            		this.setDefaultData( "alert", val );
            	},
            	deep: true
            },
            confirm: {
            	handler ( val ) {
            		this.setDefaultData( "confirm", val );
            	},
            	deep: true
            },
            prompt: {
            	handler ( val ) {
            		this.setDefaultData( "prompt", val );
            	},
            	deep: true
            }
        },
        computed: {
            containerId () {
                return this.isDefaultMode() ? this.id : "";
            },
            containerClass () {
                const cls = {
                    "tinylayer-container": true
                };
                if ( this.isDefaultMode() ) {
                    cls[ this.className ] = true;
                    cls[ "tinylayer-default" ] = true;
                }
                return cls;
            },
            containerStyle () {
                return {
                    "z-index": this.isDefaultMode() ? this.zIndex : this[ this.mode ].zIndex
                }
            },
            maskStyle () {
            	return {
            		background: this.isDefaultMode() ? this.maskBackground : this[ this.mode ].maskBackground
            	}
            },
            wrapperClass () {
                return {
                    "tinylayer-public-wrapper": true,
                    "tinylayer-fullscreen": this.fullscreenmode,
                    "tinylayer-draggable": !this.fullscreenmode && this.draggable,
                    "tinylayer-dragging": this.dragging
                }
            },
            wrapperStyle () {
                return {
                    width: this.width,
                    background: this.background,
                    left: typeof this.wrapperLeft === "number" ? `${ this.wrapperLeft }px` : this.wrapperLeft,
                    top: typeof this.wrapperTop === "number" ? `${ this.wrapperTop }px` : this.wrapperTop
                }
            },
            headerStyle () {
                return {
                    height: headerHeight,
                    "border-bottom": this.noline ? null : this.lineStyle
                }
            },
            contentStyle () {
                const topBottomMargin = this.fullscreenmode ? "0px" : "40px";
                const headerFooter = `
                    ${ this.headerVisible ? headerHeight : "0px" } - 
                    ${ this.footerVisible ? footerHeight : "0px" }
                `;
                const h = `calc(100vh - ${ topBottomMargin } - ${ headerFooter })`;
                return {
                    "background": this.contentBackground,
                    "max-height": !this.fscreen ? h : null,
                    "height": this.fscreen ? h : this.contentHeight
                }
            },
            contentLoadingStyle () {
                let h = 0;
                if ( this.headerVisible ) {
                    h = h + parseInt( headerHeight );
                }
                if ( footerHeight ) {
                    h = h + parseInt( footerHeight );
                }
                return {
                    top: this.headerVisible ? headerHeight : 0,
                    height: `calc(100% - ${ h }px)`,
                    background: this.contentLoadingBackground
                }
            },
            footerStyle () {
                return {
                    height: footerHeight,
                    "border-top": this.noline ? null : this.lineStyle
                }
            },
            fullscreenmode: {
                get () {
                    return this.fscreen;
                },
                set ( val ) {
                    this.fscreen = val;
                }
            },
            promptInputClass () {
            	return {
            		"tinylayer-prompt-input": true,
            		"tinylayer-prompt-input-error": this.prompt.isError
            	}
            }
        },
        mounted () {
            this.$nextTick(() => {
                document.body.appendChild( this.$el );
            })
        },
        methods: {
            isDefaultMode () {
                return !( /(loading|alert|confirm|prompt)/.test( this.mode ) );
            },
            resetData ( type ) {
                for ( const key in originalData[ type ] ) {
                    const value = originalData[ type ][ key ];
                    if ( util.isPlainObject( value ) ) {
                        for ( const _key in value ) {
                            this[ type ][ key ][ _key ] = value[ _key ];
                        }
                    } else {
                        this[ type ][ key ] = value;
                    }
                }
            },
            setDefaultData ( type, val ) {
            	for ( const key in originalData[ type ] ) {
            		const value = originalData[ type ][ key ];
            		if ( util.isPlainObject( value ) ) {
            			for ( const _key in value ) {
            				this[ type ][ key ][ _key ] = val[ key ][ _key ] === undefined ? value[ _key ] : val[ key ][ _key ];
            			}
            		} else {
            			this[ type ][ key ] = val[ key ] === undefined ? value : val[ key ];
            		}
	            }
            },
            publicAfterLeave ( type ) {
                this.containerIsShow = false;
                this.resetData( type );
            },
            confirmCancel () {
                this.layerIsShow = false;
                const callback = this.confirm.cancel.callback;
                if ( util.isFunction( callback ) ) {
                    callback();
                }
            },
            confirmOk () {
                this.layerIsShow = false;
                const callback = this.confirm.ok.callback;
                if ( util.isFunction( callback ) ) {
                    callback();
                }
            },
            promptCancel () {
                this.layerIsShow = false;
                const callback = this.prompt.cancel.callback;
                if ( util.isFunction( callback ) ) {
                    callback();
                }
            },
            promptOk () {
                if ( this.prompt.ok.closable ) {
                    this.layerIsShow = false;
                }
                if ( util.isRegExp( this.prompt.pattern ) ) {
                	const val = this.$refs.promptRef.value;
                    this.prompt.isError = !new RegExp( this.prompt.pattern ).test( val );
                    console.log(new RegExp( this.prompt.pattern ).test( val ))
                	util.delay().then(() => {
                		if ( this.$refs.promptRef ) {
                			this.$refs.promptRef.value = val;
                		}
                	})
                }
                const callback = this.prompt.ok.callback;
                if ( util.isFunction( callback ) ) {
                    callback( 
                        this.prompt, 
                        this.$refs.promptRef.value, 
                        this.prompt.pattern ? !this.prompt.isError : undefined 
                    );
                }
            },
            afterPromptEnter () {
                this.$refs.promptRef.focus();
            },
            fullscreenToggle () {
                this.fullscreenmode = !this.fullscreenmode;
                this.$emit( "fullscreen-change", this.fullscreenmode );
                if ( this.fullscreenmode ) {
                    cacheWrapperLeft = this.wrapperLeft;
                    cacheWrapperTop = this.wrapperTop;
                    this.wrapperLeft = "50%";
                    this.wrapperTop = "50%";
                } else {
                    if ( typeof cacheWrapperLeft === "number" && typeof cacheWrapperTop === "number" ) {
                        this.wrapperLeft = cacheWrapperLeft;
                        this.wrapperTop = cacheWrapperTop;
                    }
                }
            },
            cancel () {
                this.$emit( "cancel" );
                if ( this.cancelClosable ) {
                    this.close();
                }
            },
            ok () {
                this.$emit( "ok" );
                if ( this.okClosable ) {
                    this.close();
                }
            },
            close ( target ) {
                if ( target === "mask" ) {
                    if ( this.maskClosable && this.mode !== "loading" ) {
                        this.$emit( "change", false );
                        if ( isVue3 ) {
                            this.$emit( "update:modelValue", false );
                        }
                        if ( autoCloseTimer ) {
                			window.clearInterval( autoCloseTimer );
                		}
                    }
                } else {
                    this.$emit( "change", false );
                    if ( isVue3 ) {
                        this.$emit( "update:modelValue", false );
                    }
                    if ( autoCloseTimer ) {
	                	window.clearInterval( autoCloseTimer );
	                }
                }
            },
            afterWrapperEnter () {
                this.$emit( "after-open" );
                if ( Number.isSafeInteger( this.autoClose ) && this.autoClose > 1000 ) {
                    this.autoCloseIsShow = true;
                    let countdown = ~~this.autoClose + 1000;
                    const doing = () => {
                        countdown -= 1000;
                        if ( countdown >= 1 ) {
                            this.$emit( "auto-close-countdown", countdown, countdown / 1000 );
                            this.autoCloseText = `${ countdown / 1000 } 秒后关闭`;
                        } else {
                            window.clearInterval( autoCloseTimer );
                            this.autoCloseIsShow = false;
                            if ( !this.autoCloseIntercept ) {
                                this.close();
                            } else {
                                this.$emit( "auto-close-intercept" );
                            }
                        }
                    }
                    doing();
                    autoCloseTimer = window.setInterval( doing, 1000 );
                }
                if ( this.draggable ) {
                    let [ top, left, x, y ] = [ 0, 0, 0, 0 ];
                    this.$nextTick(() => {
                        const wrapperRef = this.$refs.wrapperRef;
                        const headerRef = this.$refs.headerRef;
                        const layerWidth = parseInt( util.getCss( wrapperRef, "width" ) );
                        const layerHeight = parseInt( util.getCss( wrapperRef, "height" ) );
                        let flag;
                        headerRef.onmousedown = event => {
                            flag = true;
                            if ( this.fullscreenmode ) {
                                return;
                            }
                            if ( layerWidth >= windowWidth ) {
                                return;
                            }
                            event.preventDefault();
                            const target = event.target;
                            if ( 
                                /^(i|svg|path)+$/.test( target.nodeName.toLowerCase() ) ||
                                target.classList.contains( "tinylayer-iconbtn" )
                            ) {
                                return;
                            }
                            this.dragging = true;
                            left = event.pageX - wrapperRef.offsetLeft;
                            top = event.pageY - wrapperRef.offsetTop;
                            document.onmousemove = ev => {
                                flag = false;
                                x = ev.pageX - left;
                                y = ev.pageY - top;
                                const [ halfLayerWidth, halfLayerHeight ] = [ layerWidth / 2, layerHeight / 2 ];
                                ( x < halfLayerWidth ) && ( x = halfLayerWidth );
                                ( y < halfLayerHeight ) && ( y = halfLayerHeight );
                                if ( x > windowWidth - halfLayerWidth ) {
                                    x = windowWidth - halfLayerWidth;
                                }
                                if ( y > windowHeight - halfLayerHeight ) {
                                    y = windowHeight - halfLayerHeight;
                                }
                                this.wrapperLeft = x;
                                this.wrapperTop = y;

                                this.$emit( "dragging", ~~( x - layerWidth / 2 ), ~~( y - layerHeight / 2 ) );
                            }
                        }
                        const mouseupHandle = () => {
                            document.onmousemove = null;
                            this.dragging = false;
                            if ( !flag ) {
                                this.$emit( "drag-end", ~~( x - layerWidth / 2 ), ~~( y - layerHeight / 2 ) );
                            }
                            flag = true;
                        }
                        headerRef.onmouseup = mouseupHandle;
                        document.onmouseup = mouseupHandle;
                    })
                }
            },
            afterWrapperLeave () {
                document.body.classList.remove( "tinylayer-body-noscroll" );
                this.$emit( "after-close" );
                this.fscreen = this.fullscreen;
                this.wrapperLeft = "50%";
                this.wrapperTop = "50%";
                cacheWrapperLeft = null;
                cacheWrapperTop = null;
                if ( this.destroyAfterClose ) {
                    this.containerIsShow = false;
                }
            }
        }
    }
</script>

<style>
    .tinylayer-fade-enter-active, 
    .tinylayer-fade-leave-active {
        transition: .28s;
    }
    .tinylayer-fade-enter, 
    .tinylayer-fade-enter-from, 
    .tinylayer-fade-leave-to {
        opacity: 0;
    }
    .tinylayer-slide-enter-active, 
    .tinylayer-slide-leave-active {
        transition: .28s;
    }
    .tinylayer-slide-enter, 
    .tinylayer-slide-enter-from, 
    .tinylayer-slide-leave-to {
        opacity: 0;
        transform: translate(-50%, -50%) scale(.88) !important;
    }
    .tinylayer-body-noscroll {
        overflow: hidden;
        padding-right: 17px;
    }
    .tinylayer-container {
        --tinylayer-public-radius: 4px;
        --tinylayer-public-padding: 15px;
        --tinylayer-theme-color: #1588fc;
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        pointer-events: none;
    }
    .tinylayer-container * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
    .tinylayer-mask {
        position: absolute;
        top: 0;
        left: 0;
        z-index: 1;
        width: 100%;
        height: 100%;
        pointer-events: auto;
    }
    .tinylayer-public-loading {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        pointer-events: auto;
    }
    .tinylayer-public-loading > div {
        position: absolute;
        top: 50%;
        left: 50%;
        z-index: 5;
        width: calc(100% - 40px);
        transform: translate(-50%, -50%);
        pointer-events: none;
    }
    .tinylayer-public-loading > div > div {
        width: 44px;
        height: 44px;
        margin: 0 auto;
        overflow: hidden;
    }
    .tinylayer-public-loading > div > p {
        display: block;
        width: 100%;
        height: 20px;
        line-height: 20px;
        font-size: 14px;
        user-select: none;
        text-align: center;
        margin-top: 5px;
        color: var(--tinylayer-theme-color);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .tinylayer-public-loading svg {
        width: 44px;
        transform-origin: center;
        animation: tinylayer-public-loading-svg-animation 2s linear infinite;
    }
    .tinylayer-public-loading circle {
        fill: none;
        stroke: var(--tinylayer-theme-color);
        stroke-width: 2;
        stroke-dasharray: 1, 200;
        stroke-dashoffset: 0;
        stroke-linecap: round;
        animation: tinylayer-public-loading-circle-animation 1.6s ease-in-out infinite;
    }
    @keyframes tinylayer-public-loading-svg-animation {
        100% {
            transform: rotate(360deg);
        }
    }
    @keyframes tinylayer-public-loading-circle-animation {
        0% {
            stroke-dasharray: 1, 200;
            stroke-dashoffset: 0;
        }
        50% {
            stroke-dasharray: 90, 200;
            stroke-dashoffset: -35px;
        }
        100% {
            stroke-dashoffset: -125px;
        }
    }
    .tinylayer-public-wrapper {
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        z-index: 5;
        box-shadow: rgba(0, 0, 0, .1) 0 0 15px;
        background: #fff;
        border-radius: var(--tinylayer-public-radius);
        pointer-events: auto;
        min-width: 300px;
    }
    .tinylayer-public-wrapper.tinylayer-fullscreen {
        width: 100% !important;
        border-radius: 0 !important;
    }
    .tinylayer-public-button-box {
        display: flex;
        align-items: center;
        justify-content: flex-end;
    }
    .tinylayer-public-button-box button {
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: flex-start;
        height: 32px;
        padding: 0 15px;
        font-size: 14px;
        margin-left: 7px;
        border-radius: var(--tinylayer-public-radius);
        user-select: none;
        outline: none;
        transition: .2s;
    }
    .tinylayer-public-button-box button[disabled] {
        cursor: not-allowed;
        opacity: .55;
    }
    .tinylayer-public-button-ok {
        color: #fff;
        border: var(--tinylayer-theme-color) solid 1px;
        background: var(--tinylayer-theme-color);
    }
    .tinylayer-public-button-ok:not([disabled]):hover {
        opacity: .7;
    }
    .tinylayer-public-button-ok:not([disabled]):active {
        opacity: 1;
    }
    .tinylayer-public-button-ok > i {
        position: relative;
        display: block;
        width: 16px;
        height: 16px;
        margin-right: 6px;
        pointer-events: none;
        animation: 2s tinylayer-button-loading-animation linear infinite;
    }
    @keyframes tinylayer-button-loading-animation {
        from  {
            transform: rotate(0deg);
        }
        100% {
            transform: rotate(360deg);
        }
    }
    .tinylayer-public-button-cancel {
        color: #777;
        border: #d0d0d0 solid 1px;
        background: #fff;
    }
    .tinylayer-public-button-cancel:not([disabled]):hover {
        border-color: var(--tinylayer-theme-color);
        color: var(--tinylayer-theme-color);
    }
    .tinylayer-prompt {
        width: 370px;
        padding: 20px;
    }
    .tinylayer-prompt-title {
        font-size: 16px;
        color: #000;
        user-select: none;
    }
    .tinylayer-prompt-input {
        margin: 20px 0 7px;
    }
    .tinylayer-prompt-input input {
        display: block;
        width: 100%;
        height: 34px;
        border-radius: var(--tinylayer-public-radius);
        padding: 0 10px;
        font-size: 14px;
        border: #e0e0e0 solid 1px;
        outline: none;
        transition: .2s;
    }
    .tinylayer-prompt-input input:not([disabled]):hover,
    .tinylayer-prompt-input input:not([disabled]):focus {
        border-color: var(--tinylayer-theme-color);
    }
    .tinylayer-prompt-input input::-webkit-input-placeholder {
        color: #bbb;
        transform: translateY(0);
    }
    .tinylayer-prompt-input input[disabled] {
        cursor: not-allowed;
        background: #f1f1f1;
    }
    .tinylayer-prompt-input input[disabled]::selection {
        background: transparent;
    }
    .tinylayer-prompt-input.tinylayer-prompt-input-error input {
    	border-color: #db0505 !important;
    }
    .tinylayer-error-msg {
    	color: #db0505;
    	word-break: break-all;
    	font-size: 13px;
    }
    .tinylayer-prompt .tinylayer-public-button-box {
    	margin-top: 20px;
    }
    .tinylayer-alert-confirm {
        width: 400px;
        padding: 20px 25px;
    }
    .tinylayer-alert-confirm-header {
        height: 40px;
        display: flex;
        align-items: center;
        justify-content: flex-start;
    }
    .tinylayer-alert-confirm-header i {
        width: 26px;
        height: 26px;
        border-radius: 50%;
        margin-right: 14px;
    }
    .tinylayer-alert-confirm-warning {
        transform: rotate(180deg);
    }
    .tinylayer-alert-confirm-header p {
        width: calc(100% - 34px);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        color: #000;
        font-weight: bold;
        font-size: 16px;
    }
    .tinylayer-alert-confirm-content {
        padding-left: 40px;
        font-size: 14px;
        word-break: break-all;
        color: #2f2f2f;
        max-height: 400px;
        overflow: hidden;
    }
    .tinylayer-alert-confirm-button {
        display: flex;
        align-items: center;
        justify-content: flex-end;
        margin-top: 40px;
    }
    .tinylayer-draggable .tinylayer-header {
        cursor: grab;
    }
    .tinylayer-draggable.tinylayer-dragging .tinylayer-header {
        cursor: grabbing;
    }
    .tinylayer-fullscreen .tinylayer-header, 
    .tinylayer-fullscreen .tinylayer-footer {
        border-radius: 0;
    }
    .tinylayer-header, 
    .tinylayer-footer {
        overflow: hidden;
        padding: 0 var(--tinylayer-public-padding);
        display: flex;
        align-items: center;
    }
    .tinylayer-header {
        justify-content: space-between;
        border-radius: var(--tinylayer-public-radius) var(--tinylayer-public-radius) 0 0;
        user-select: none;
    }
    .tinylayer-title {
        display: flex;
        align-items: center;
        justify-content: flex-start;
        width: calc(100% - 60px);
        height: 20px;
        font-size: 16px;
        font-weight: bold;
        white-space: nowrap;
        overflow: hidden;
    }
    .tinylayer-iconbtn {
        display: flex;
        justify-content: flex-end;
        cursor: default;
    }
    .tinylayer-icon {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 16px;
        height: 16px;
        cursor: pointer;
        user-select: none;
        opacity: .5;
        transform: scale(.9);
    }
    .tinylayer-icon:not(:first-child) {
        margin-left: 14px;
    }
    .tinylayer-icon:hover {
        opacity: .9;
    }
    .tinylayer-content {
        position: relative;
        min-height: 110px;
        padding: var(--tinylayer-public-padding);
        border-radius: var(--tinylayer-public-radius);
        overflow-x: hidden;
        overflow-y: auto;
    }
    .tinylayer-content-iframe {
        width: 100%;
        height: 100%;
        border: none;
    }
    .tinylayer-footer {
        justify-content: space-between;
        border-radius: 0 0 var(--tinylayer-public-radius) var(--tinylayer-public-radius);
    }
    .tinylayer-autoclose {
    	display: flex;
    	align-items: center;
    	justify-content: flex-start;
    	font-size: 13px;
    	user-select: none;
    }
    .tinylayer-autoclose span {
    	height: 20px;
    	line-height: 20px;
    }
    .tinylayer-autoclose span:first-child {
    	width: 30px;
    }
</style>